#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2019 Huawei
# GNU General Public License v3.0+ (see COPYING or
# https://www.gnu.org/licenses/gpl-3.0.txt)
<%# lines(autogen_notice :python) -%>

<% cfs = custom_override_methods(object) -%>
<% unless cfs.empty? -%>
from ansible.modules.cloud.<%= module_dir %>.custom_functions import (
<%   last_item = cfs.pop -%>
<%   cfs.each do |i| -%>
    <%= i %>,
<%   end -%>
    <%= last_item %>)

<% end -%>
from ansible.module_utils.hwc_utils import (
<%= module_import_items(object) %>

<%
  metadata_version = quote_string(@config.manifest.get('metadata_version',
                                                       config))
  supported_by = quote_string(@config.manifest.get('supported_by', config))
-%>
ANSIBLE_METADATA = {'metadata_version': <%= metadata_version -%>,
                    'status': <%= @config.manifest.get('status', config) -%>,
                    'supported_by': <%= supported_by -%>}

DOCUMENTATION = '''
---
module: <%= module_name(object) %>
description:
<%= lines(indent(bullet_lines(object.description, 4), 4)) -%>
short_description: Creates a resource of <%= product_name.capitalize %>/<%= object.name %> in <%= cloud_name %>
version_added: <%= lines(@config.manifest.get('version_added', config)) -%>
author: <%= lines(@config.manifest.get('author', config)) -%>
requirements:
<% @config.manifest.get('requirements', config).each do |line| -%>
<%= lines(indent(bullet_line(line, 4, false, false), 4)) -%>
<% end -%>
options:
    state:
        description:
            - Whether the given object should exist in <%= cloud_name %>.
        choices: ['present', 'absent']
        default: 'present'
    filters:
        description:
            - A list of filters to apply when deciding whether existing
              resources match and should be altered. The item of filters
              is the name of input options.
        required: true
<% at = async_timout(object) -%>
<% unless at.empty? -%>
    timeouts:
        description:
            - The timeouts for each operations.
        type: dict
        suboptions:
<%   unless at["create"].nil? -%>
            create:
                description:
                    - The timeouts for create operation.
                type: str
                default: <%= sprintf('\'%dm\'', at["create"] / 60) %>
<%   end -%>
<%   unless at["update"].nil? -%>
            update:
                description:
                    - The timeouts for update operation.
                type: str
                default: <%= sprintf('\'%dm\'', at["update"] / 60) %>
<%   end -%>
<%   unless at["delete"].nil? -%>
            delete:
                description:
                    - The timeouts for delete operation.
                type: str
                default: <%= sprintf('\'%dm\'', at["delete"] / 60) %>
<%   end -%>
<% end -%>
<% order_properties(object.not_output_properties).each do |prop| -%>
<%= lines(indent(doc_property_yaml(prop, object, 4), 4)) -%>
<% end -%>
extends_documentation_fragment: hwc
'''

<%if example -%>
EXAMPLES = '''
<% unless example.description.nil? -%>
# <%= example.description %>
<% end -%>
<% if example.dependencies -%>
<%   example.dependencies.each do |depend| -%>
<%= lines(depend.build_example('present', object)) -%>
<%   end # example.dependencies.each -%>
<% end # if example.dependencies -%>
<%= lines(example.task.build_example('present', object)) -%>
'''

<% end -%>
RETURN = '''
<% order_properties(object.all_user_properties).each do |prop| -%>
<%= lines(indent(return_property_yaml(prop, 4), 4)) -%>
<% end -%>
'''


def build_module():
<%
  mod_props = order_properties(object.not_output_properties).map do |prop|
    python_dict_for_property(prop, object, 12)
  end
-%>
    return HwcModule(
        argument_spec=dict(
            state=dict(default='present', choices=['present', 'absent'],
                       type='str'),
            filters=dict(required=True, type='list', elements='str'),
<% unless at.empty? -%>
            timeouts=dict(type='dict', options=dict(
<%   unless at["create"].nil? -%>
                create=dict(default=<%= sprintf('\'%dm\'', at["create"] / 60) -%>, type='str'),
<%   end -%>
<%   unless at["update"].nil? -%>
                update=dict(default=<%= sprintf('\'%dm\'', at["update"] / 60) -%>, type='str'),
<%   end -%>
<%   unless at["delete"].nil? -%>
                delete=dict(default=<%= sprintf('\'%dm\'', at["delete"] / 60) -%>, type='str'),
<%   end -%>
            ), default=dict()),
<% end -%>
<%= lines(indent_list(mod_props, 0)) -%>
        ),
        supports_check_mode=True,
    )


def main():
    """Main function"""

    module = build_module()
    config = Config(module, "<%= object.__product.prefix[1..-1] %>")

<% list_api = object.list_api
   raise "no list api" if list_api.nil?
-%>
    try:
        _init(config)
        is_exist = module.params['id']

        result = None
        changed = False
        if module.params['state'] == 'present':
            if not is_exist:
                if not module.check_mode:
                    create(config)
                changed = True

            inputv = user_input_parameters(module)
            resp, array_index = read_resource(config)
            result = build_state(inputv, resp, array_index)
<% if updatable?(object) -%>
            set_readonly_options(inputv, result)
            if are_different_dicts(inputv, result):
                if not module.check_mode:
                    update(config, inputv, result)

                    inputv = user_input_parameters(module)
                    resp, array_index = read_resource(config)
                    result = build_state(inputv, resp, array_index)
                    set_readonly_options(inputv, result)
                    if are_different_dicts(inputv, result):
                        raise Exception("Update resource failed, "
                                        "some attributes are not updated")

                changed = True

<% end # updateable(object)? -%>
            result['id'] = module.params.get('id')
        else:
            result = dict()
            if is_exist:
                if not module.check_mode:
                    delete(config)
                changed = True

    except Exception as ex:
        module.fail_json(msg=str(ex))

    else:
        result['changed'] = changed
        module.exit_json(**result)


def _init(config):
    module = config.module
    if module.params['id']:
        return

    v = search_resource(config)
    n = len(v)
    if n > 1:
        raise Exception("find more than one resources(%s)" % ", ".join([
            navigate_value(i, [<%= index2navigate(list_api.resource_id_path) %>])
            for i in v
        ]))

    if n == 1:
        module.params['id'] = navigate_value(v[0], [<%= index2navigate(list_api.resource_id_path) %>])


def user_input_parameters(module):
    return {
<% object.not_output_properties.each do |prop| -%>
        "<%= prop.out_name %>": module.params.get("<%= prop.out_name %>"),
<% end -%>
    }


<% united_async, gasync = is_united_async(object) -%>
<% async_wait_name = "async_wait" -%>
<% create_api = object.create_api -%>
def create(config):
    module = config.module
    client = config.client(<%= argu_for_sdkclient(create_api) %>)
<% unless at["create"].nil? -%>
    timeout = 60 * int(module.params['timeouts']['create'].rstrip('m'))
<% end -%>
    opts = user_input_parameters(module)
    opts["ansible_module"] = module

    params = build_create_parameters(opts)
    r = send_create_request(module, params, client)
<% pre_api = create_api -%>
<% if create_api.async.nil? -%>
<%   path, array_index = convert_resource_id_path(create_api.resource_id_path) -%>
<%   if array_index.nil? -%>
    module.params['id'] = navigate_value(r, [<%= index2navigate(path) %>])
<%   else -%>
<%     if array_index.length == 1 -%>
<%       k1 = array_index.keys[0] -%>
    module.params['id'] = navigate_value(r, [<%= index2navigate(path) %>],
                                         {"<%= k1 %>": <%= array_index[k1] %>})
<%     else -%>
    array_index = {
        <% array_index.each_pair do |k, v| -%>
        "<%= k %>": <%= v %>,
        <% end -%>
    }
    module.params['id'] = navigate_value(r, [<%= index2navigate(path) %>],
                                         array_index)
<%     end -%>
<%   end -%>
<% else -%>
<%   async_op = create_api.async.operation -%>
<%   ncl = need_build_new_client(pre_api, async_op) -%>
<%   if ncl -%>

    client1 = config.client(<%= argu_for_sdkclient(async_op) %>)
<%   end -%>
    obj = <%= united_async ? async_wait_name : "async_wait_create" %>(config, r, <%= ncl ? "client1" : "client" %>, timeout)
<%   async_r = create_api.async.result -%>
<%   unless async_r.sub_jobs_path.nil? %>
    sub_job_identity = {
<%     async_r.sub_job_identity.each do |k, v| -%>
        "<%= k %>": <%= convert_simple_value(v) %>,
<%     end -%>
    }
    for item in navigate_value(obj, [<%= index2navigate(async_r.sub_jobs_path) %>]):
        for k, v in sub_job_identity.items():
            if item[k] != v:
                break
        else:
            obj = item
            break
    else:
        raise Exception("Can't find the sub job")
<%   end -%>
    module.params['id'] = navigate_value(obj, [<%= index2navigate(async_r.field) %>])
<% end -%>
<% other_api(object, "c").each do | item | -%>
<%
     r = item.async.nil? ? "" : "r = "
     spaces = item.parameters.nil? ? "" : "    "
-%>

<%   unless item.parameters.nil? -%>
    params = build_<%= item.out_name %>_parameters(opts)
    if params:
<%   end -%>
<%   ncl = need_build_new_client(pre_api, item) -%>
<%   if ncl -%>
    <%= spaces %>client1 = config.client(<%= argu_for_sdkclient(item) %>)
<%   end -%>
    <%= spaces %><%= r %>send_<%= item.out_name %>_request(module, <%= item.parameters.nil? ? "None" : "params" %>, <%= ncl ? "client1" : "client" %>)
<%   unless item.async.nil? -%>
<%     async_op = item.async.operation -%>
<%     ncl1 = need_build_new_client(item, async_op) -%>
<%     if ncl1 -%>

    <%= spaces %>client1 = config.client(<%= argu_for_sdkclient(async_op) %>)
<%     end -%>
    <%= spaces %><%= united_async ? async_wait_name : "async_wait_" + item.out_name %>(config, r, <%= ncl1 || ncl ? "client1" : "client" %>, timeout)
<%   end -%>
<% end -%>
<% if updatable?(object) -%>
<%   update_api = object.update_api -%>
<%   update_apis = [update_api, other_api(object, "u"), multi_invoke_api(object, "u")] -%>
<%   multi_update = update_apis.flatten.compact.length > 1 -%>


def update(config, expect_state, current_state):
    module = config.module
    expect_state["current_state"] = current_state
    current_state["current_state"] = current_state
<%   unless update_api.nil? -%>
    client = config.client(<%= argu_for_sdkclient(update_api) %>)
<%   end -%>
<%   unless at["update"].nil? -%>
    timeout = 60 * int(module.params['timeouts']['update'].rstrip('m'))
<%   end -%>
<%   pre_api = update_api -%>
<%   unless update_api.nil? -%>
<%     r = update_api.async.nil? ? "" : "r = " -%>

    params = build_update_parameters(expect_state)
<%     if multi_update -%>
    params1 = build_update_parameters(current_state)
    if params and are_different_dicts(params, params1):
<%     else -%>
    if params:
<%     end -%>
        <%= r %>send_update_request(module, params, client)
<%     unless update_api.async.nil? -%>
<%       async_op = update_api.async.operation -%>
<%       ncl = need_build_new_client(pre_api, async_op) -%>
<%       if ncl -%>

        client1 = config.client(<%= argu_for_sdkclient(async_op) %>)
<%       end -%>
        <%= united_async ? async_wait_name : "async_wait_update" %>(config, r, <%= ncl ? "client1" : "client" %>, timeout)
<%     end -%>
<%   end # update_api.nil? -%>
<%   other_updates = other_api(object, "u") -%>
<%   if !other_updates.empty? && update_api.nil? -%>
<%     pre_api = other_updates[0] -%>
    client = config.client(<%= argu_for_sdkclient(pre_api) %>)
<%   end -%>
<%   other_updates.each do | item | -%>
<%
       r = item.async.nil? ? "" : "r = "
       spaces = item.parameters.nil? ? "" : "    "
-%>

<%     unless item.parameters.nil? -%>
    params = build_<%= item.out_name %>_parameters(expect_state)
<%       if multi_update -%>
    params1 = build_<%= item.out_name %>_parameters(current_state)
    if params and are_different_dicts(params, params1):
<%       else -%>
    if params:
<%       end -%>
<%     end -%>
<%     ncl = need_build_new_client(pre_api, item) -%>
<%     if ncl -%>
    <%= spaces %>client1 = config.client(<%= argu_for_sdkclient(item) %>)
<%     end -%>
    <%= spaces %><%= r %>send_<%= item.out_name %>_request(module, <%= item.parameters.nil? ? "None" : "params" %>, <%= ncl ? "client1" : "client" %>)
<%     unless item.async.nil? -%>
<%       async_op = item.async.operation -%>
<%       ncl1 = need_build_new_client(item, async_op) -%>
<%       if ncl1 -%>

    <%= spaces %>client1 = config.client(<%= argu_for_sdkclient(async_op) %>)
<%       end -%>
    <%= spaces %><%= united_async ? async_wait_name : "async_wait_" + item.out_name %>(config, r, <%= ncl1 || ncl ? "client1" : "client" %>, timeout)
<%     end -%>
<%   end -%>
<%   multi_invoke_api(object, "u").each do | item | -%>

<%     ncl = need_build_new_client(pre_api, item) -%>
<%     if ncl -%>
    client1 = config.client(<%= argu_for_sdkclient(item) %>)
<%     end -%>
    multi_invoke_<%= item.out_name %>(config, expect_state, <%= ncl ? "client1" : "client" %><%= item.async.nil? ? "" : ", timeout"%>)
<%   end -%>
<% end # updatable? -%>


<% read_api = object.read_api -%>
<% delete_api = object.delete_api -%>
def delete(config):
    module = config.module
    client = config.client(<%= argu_for_sdkclient(delete_api) %>)
<% unless at["delete"].nil? -%>
    timeout = 60 * int(module.params['timeouts']['delete'].rstrip('m'))
<% end -%>
<%
   r = delete_api.async.nil? ? "" : "r = "
   spaces = delete_api.parameters.nil? ? "" : "    "
-%>

<% unless delete_api.parameters.nil? -%>
    opts = user_input_parameters(module)
    opts["ansible_module"] = module

    params = build_delete_parameters(opts)
    if params:
<% end -%>
    <%= spaces %><%= r %>send_delete_request(module, <%= delete_api.parameters.nil? ? "None" : "params" %>, client)
<% unless delete_api.async.nil? -%>
<%   async_op = delete_api.async.operation -%>
<%   if need_build_new_client(delete_api, async_op) -%>

    <%= spaces %>client = config.client(<%= argu_for_sdkclient(async_op) %>)
<%   end -%>
    <%= spaces %><%= united_async ? async_wait_name : "async_wait_delete" %>(config, r, client, timeout)
<% else -%>
<%   unless create_api.async.nil? -%>

<%   # if create api is async, then it is best to make sure the resource is delete successfully -%>
<%     if !read_api.nil? -%>
    url = build_path(module, "<%= read_api.path -%>")
<%     end -%>
<%     if need_build_new_client(delete_api, read_api) -%>
    client = config.client(<%= argu_for_sdkclient(read_api) %>)
<%     end -%>

    def _refresh_status():
        try:
            client.get(url)
        except HwcClientException404:
            return True, "Done"

        except Exception:
            return None, ""

        return True, "Pending"

    timeout = 60 * int(module.params['timeouts']['create'].rstrip('m'))
    try:
        wait_to_finish(["Done"], ["Pending"], _refresh_status, timeout)
    except Exception as ex:
        module.fail_json(msg="module(<%= module_name(object) %>): error "
                             "waiting for api(delete) to "
                             "be done, error= %s" % str(ex))
<%   end -%>
<% end -%>


def read_resource(config):
    module = config.module
    client = config.client(<%= argu_for_sdkclient(read_api) %>)

    res = {}
<% read_apis = [read_api, other_api(object, "r")].flatten.compact -%>
<% array_params = Hash.new -%>
<% pre_api = read_api -%>
<% read_apis.each do | item | -%>

<%   ncl = need_build_new_client(item, pre_api) -%>
<%   if ncl -%>
    client1 = config.client(<%= argu_for_sdkclient(item) %>)
<%   end -%>
    r = send_<%= item.out_name%>_request(module, <%= ncl ? "client1" : "client" %>)
    res["<%= item.name %>"] = fill_<%= item.out_name%>_resp_body(r)
<%   v = get_fixed_length_array_parameter(item) -%>
<%   unless v.nil? || v.empty? -%>
<%     v.each_pair do |k, v1| -%>
<%       array_params[item.name + "." + k] = v1 if v1 == 1 -%>
<%     end-%>
<%   end -%>
<% end -%>
<% unless array_params.empty? -%>

    array_index = {
<%   array_params.each_pair do |k, v| -%>
        "<%= k %>": 0,
<%   end -%>
    }
<% end -%>

    return res, <%= array_params.empty? ? "None" : "array_index" %>


def build_state(opts, response, array_index):
    states = flatten_options(response, array_index)
<% unless unreadable_properties(object).empty? -%>
    set_unreadable_options(opts, states)
<% end -%>
<% unless properties_to_adjust(object).empty? -%>
    adjust_options(opts, states)
<% end -%>
    return states
<% ql = build_list_query_params(list_api, 4) -%>
<% unless ql.empty? -%>


def _build_query_link(opts):
<%=  lines(ql) -%>

    return query_link
<% end -%>


def search_resource(config):
    module = config.module
    client = config.client(<%= argu_for_sdkclient(list_api) %>)
    opts = user_input_parameters(module)
    identity_obj = _build_identity_object(module, opts)
<% unless ql.empty? -%>
    query_link = _build_query_link(opts)
<% end -%>
<% if list_api.path.include?("{") -%>
    link = build_path(module, "<%= list_api.path %>")<%= ql.empty? ? "" : " + query_link" %>
<% else -%>
    link = <%= sprintf("\"%s\"", list_api.path) %><%= ql.empty? ? "" : " + query_link" %>
<% end -%>

    result = []
<% no_need_adjust = list_api_parameters_to_adjust(list_api, object).empty? -%>
<% _, pp = list_api.pagination_param -%>
<% if pp.eql?("marker") -%>
    p = {'marker': ''}
    while True:
        url = link.format(**p)
        r = send_<%= list_api.out_name %>_request(module, client, url)
        if not r:
            break

        for item in r:
            item = fill_<%= list_api.out_name%>_resp_body(item)
<%   unless no_need_adjust -%>
            adjust_list_resp(identity_obj, item)
<%   end -%>
            if not are_different_dicts(identity_obj, item):
                result.append(item)

        if len(result) > 1:
            break

        p['marker'] = r[-1].get(<%= quote_string(list_api.resource_id_path) -%>)
<% elsif pp.eql?('offset') -%>
    p = {'offset': 0}
    while True:
        url = link.format(**p)
        r = send_<%= list_api.out_name %>_request(module, client, url)
        if not r:
            break

        for item in r:
            item = fill_<%= list_api.out_name%>_resp_body(item)
<%   unless no_need_adjust -%>
            adjust_list_resp(identity_obj, item)
<%   end -%>
            if not are_different_dicts(identity_obj, item):
                result.append(item)

        if len(result) > 1:
            break

        p['offset'] += 1
<% elsif pp.eql?('start') -%>
    p = {'start': 0}
    while True:
        url = link.format(**p)
        r = send_<%= list_api.out_name %>_request(module, client, url)
        if not r:
            break

        for item in r:
            item = fill_<%= list_api.out_name%>_resp_body(item)
<%   unless no_need_adjust -%>
            adjust_list_resp(identity_obj, item)
<%   end -%>
            if not are_different_dicts(identity_obj, item):
                result.append(item)

        if len(result) > 1:
            break

        p['start'] += len(r)
<% else -%>
    r = send_<%= list_api.out_name %>_request(module, client, link)
    if not r:
        return result

    for item in r:
        item = fill_<%= list_api.out_name%>_resp_body(item)
<%   unless no_need_adjust -%>
        adjust_list_resp(identity_obj, item)
<%   end -%>
        if not are_different_dicts(identity_obj, item):
            result.append(item)
<% end -%>

    return result
<% req_apis = req_apis(object) -%>
<% not_read_api(object).each do | k, item | -%>
<%   unless item.parameters.nil? -%>


<%= lines(build_request_body_method(object, item)) -%>
<%   end -%>
<%   next unless req_apis.include?(k) -%>


<%= lines(build_send_request_method(item, module_name(object))) -%>
<%   unless item.async.nil? || united_async -%>


<%= lines(build_resource_async_op(item, module_name(object))) -%>
<%   end -%>
<% end -%>
<% if united_async -%>


<%= lines(build_resource_async_op(nil, module_name(object), gasync)) -%>
<% end -%>
<% multi_invoke_api(object, "u").each do |item| -%>


<%= lines(build_multi_invoke_method(item, united_async)) -%>
<% end -%>
<% read_apis.each do | item | -%>


<%= lines(build_read_method(item, module_name(object), false)) -%>


<%= lines(build_fill_resp_body_method(item)) -%>
<% end -%>


<%= lines(build_convert_opts_method(object)) -%>
<%= lines(build_adjust_opts_method(object)) -%>
<%= lines(build_set_unread_opts_method(object)) -%>
<%= lines(build_set_readonly_opts_method(object)) -%>


<%= lines(build_read_method(list_api, module_name(object), true)) -%>


<%= lines(build_identity_object_method(object, list_api)) -%>


<%= lines(build_fill_resp_body_method(list_api)) -%>
<%= lines(build_adjust_list_resp_method(list_api, object)) -%>


if __name__ == '__main__':
    main()
